23106
4920
프로그래밍 언어 책에서는이 두 가지가 무엇인지 설명하지 않고 값 유형이 스택에 생성되고 참조 유형이 힙에 생성된다고 설명합니다. 나는 이것에 대한 명확한 설명을 읽지 못했습니다. 나는 스택이 무엇인지 이해합니다. 그러나,
그것들은 어디에 있고 무엇입니까 (물리적으로 실제 컴퓨터의 메모리에 있음)?
OS 또는 언어 런타임에 의해 어느 정도 제어됩니까?
그들의 범위는 무엇입니까?
각각의 크기를 결정하는 것은 무엇입니까?
더 빨리 만드는 것은 무엇입니까? 
스택은 실행 스레드를위한 스크래치 공간으로 따로 설정된 메모리입니다. 함수가 호출되면 스택 맨 위에 지역 변수와 일부 부기 데이터를 위해 블록이 예약됩니다. 해당 함수가 반환되면 블록은 사용되지 않으며 다음에 함수를 호출 할 때 사용할 수 있습니다. 스택은 항상 LIFO (last in first out) 순서로 예약됩니다. 가장 최근에 예약 된 블록은 항상 해제 될 다음 블록입니다. 이렇게하면 스택을 추적하는 것이 정말 간단 해집니다. 스택에서 블록을 해제하는 것은 하나의 포인터를 조정하는 것입니다.
힙은 동적 할당을 위해 따로 설정된 메모리입니다. 스택과 달리 힙에서 블록을 할당 및 할당 해제하는 데 적용되는 패턴이 없습니다. 언제든지 블록을 할당하고 해제 할 수 있습니다. 이로 인해 주어진 시간에 힙의 어떤 부분이 할당되었는지 또는 해제되었는지 추적하는 것이 훨씬 더 복잡해집니다. 다양한 사용 패턴에 대해 힙 성능을 조정하는 데 사용할 수있는 많은 사용자 지정 힙 할당자가 있습니다.
각 스레드는 스택을 가져 오지만 일반적으로 애플리케이션에 대해 하나의 힙만 있습니다 (다양한 유형의 할당에 대해 여러 힙을 갖는 것은 드문 일이 아닙니다).
질문에 직접 답변하려면 :
OS 또는 언어 런타임에 의해 어느 정도까지 제어됩니까?
OS는 스레드가 생성 될 때 각 시스템 수준 스레드에 대해 스택을 할당합니다. 일반적으로 OS는 응용 프로그램에 대한 힙을 할당하기 위해 언어 런타임에서 호출됩니다.
그들의 범위는 무엇입니까?
스택은 스레드에 연결되므로 스레드가 종료되면 스택이 회수됩니다. 힙은 일반적으로 런타임에 응용 프로그램 시작시 할당되며 응용 프로그램 (기술적 프로세스)이 종료 될 때 회수됩니다.
각각의 크기를 결정하는 것은 무엇입니까?
스택의 크기는 스레드가 생성 될 때 설정됩니다. 힙의 크기는 애플리케이션 시작시 설정되지만 공간이 필요하면 커질 수 있습니다 (할당자가 운영 체제에서 더 많은 메모리를 요청 함).
더 빨리 만드는 것은 무엇입니까?
스택은 액세스 패턴이 메모리를 할당하고 할당 해제하는 것을 사소하게 만들기 때문에 (포인터 / 정수는 단순히 증가 또는 감소됨) 힙은 할당 또는 할당 해제와 관련된 훨씬 더 복잡한 부기를 가지고 있습니다. 또한 스택의 각 바이트는 매우 자주 재사용되는 경향이 있으므로 프로세서의 캐시에 매핑되는 경향이있어 매우 빠릅니다. 힙에 대한 또 다른 성능 저하는 대부분 글로벌 리소스 인 힙이 일반적으로 다중 스레딩 안전해야한다는 것입니다. 즉, 각 할당 및 할당 해제는 일반적으로 프로그램의 "모든"다른 힙 액세스와 동기화되어야합니다.
명확한 시연 :
이미지 출처 : vikashazrati.wordpress.com
|
스택:
힙처럼 컴퓨터 RAM에 저장됩니다.
스택에 생성 된 변수는 범위를 벗어나 자동으로 할당 해제됩니다.
힙의 변수에 비해 할당하는 것이 훨씬 빠릅니다.
실제 스택 데이터 구조로 구현됩니다.
매개 변수 전달에 사용되는 로컬 데이터, 리턴 주소를 저장합니다.
스택을 너무 많이 사용하면 스택 오버플로가 발생할 수 있습니다 (대부분 무한 또는 너무 깊은 재귀, 매우 큰 할당).
스택에 생성 된 데이터는 포인터없이 사용할 수 있습니다.
컴파일 시간 전에 할당해야하는 데이터의 양을 정확히 알고 있고 너무 크지 않은 경우 스택을 사용합니다.
일반적으로 프로그램이 시작될 때 이미 결정된 최대 크기가 있습니다.
더미:
스택처럼 컴퓨터 RAM에 저장됩니다.
C ++에서 힙의 변수는 수동으로 삭제해야하며 범위를 벗어나지 않아야합니다. 데이터는 delete, delete [] 또는 free로 해제됩니다.
스택의 변수에 비해 할당 속도가 느립니다.
프로그램에서 사용할 데이터 블록을 할당하기 위해 요청시 사용됩니다.
할당 및 할당 해제가 많은 경우 조각화가 발생할 수 있습니다.
C ++ 또는 C에서 힙에 생성 된 데이터는 포인터로 가리키고 각각 new 또는 malloc으로 할당됩니다.
너무 큰 버퍼를 할당하도록 요청하면 할당 실패가 발생할 수 있습니다.
런타임에 필요한 데이터의 양을 정확히 모르거나 많은 데이터를 할당해야하는 경우 힙을 사용합니다.
메모리 누수를 담당합니다.
예:
int foo ()
{
char * pBuffer; // <-아직 할당 된 것이 없습니다 (스택에서 여기에 할당 된 포인터 자체 제외).
부울 b = 참; // 스택에 할당됩니다.
if (b)
{
// 스택에 500 바이트 생성
문자 버퍼 [500];
// 힙에 500 바이트 생성
pBuffer = 새 문자 [500];
} // <-여기서 버퍼 할당이 해제되고 pBuffer는 할당되지 않습니다.
} // <--- 죄송합니다. 메모리 누수가 있습니다. delete [] pBuffer를 호출 했어야합니다.
|
가장 중요한 점은 힙과 스택이 메모리를 할당 할 수있는 방법에 대한 일반적인 용어라는 것입니다. 여러 가지 방법으로 구현할 수 있으며 용어는 기본 개념에 적용됩니다.
항목 스택에서 항목은 배치 된 순서대로 다른 항목 위에 놓여 있으며 상단 항목 만 제거 할 수 있습니다.(전체를 넘어 뜨리지 않고).
스택의 단순성은 할당 된 메모리의 각 섹션에 대한 레코드를 포함하는 테이블을 유지할 필요가 없다는 것입니다. 필요한 유일한 상태 정보는 스택 끝에 대한 단일 포인터입니다. 할당 및 할당 해제하려면 해당 단일 포인터를 늘리거나 줄입니다. 참고 : 스택은 때때로 메모리 섹션의 상단에서 시작하여 위쪽으로 커지는 대신 아래쪽으로 확장되도록 구현 될 수 있습니다.
힙에는 항목이 배치되는 방식에 특별한 순서가 없습니다. 명확한 '상위'항목이 없기 때문에 어떤 순서로든 항목을 찾아 제거 할 수 있습니다.
힙 할당에는 할당 된 메모리와 할당되지 않은 메모리에 대한 전체 기록을 유지해야하며 조각화를 줄이고 요청 된 크기에 맞을만큼 큰 연속 메모리 세그먼트를 찾는 등의 오버 헤드 유지 관리가 필요합니다. 여유 공간을 남겨두고 언제든지 메모리를 할당 해제 할 수 있습니다. 때때로 메모리 할당자는 할당 된 메모리를 이동하여 메모리 조각 모음 또는 가비지 수집과 같은 유지 관리 작업을 수행합니다. 런타임시 메모리가 더 이상 범위에없는 경우 식별하고 할당을 해제합니다.
이러한 이미지는 스택과 힙에서 메모리를 할당하고 해제하는 두 가지 방법을 설명하는 데 상당히 효과적입니다. 냠!
OS 또는 언어 런타임에 의해 어느 정도까지 제어됩니까?
언급했듯이 힙과 스택은 일반적인 용어이며 여러 가지 방법으로 구현할 수 있습니다. 컴퓨터 프로그램에는 일반적으로 호출 된 함수에 대한 포인터와 같은 현재 함수와 관련된 정보와 모든 지역 변수를 저장하는 호출 스택이라는 스택이 있습니다. 함수가 다른 함수를 호출 한 다음 반환하기 때문에 스택은 함수의 정보를 호출 스택 아래로 유지하기 위해 확장 및 축소됩니다. 프로그램은 실제로 런타임을 제어하지 않습니다. 프로그래밍 언어, OS 및 시스템 아키텍처에 따라 결정됩니다.
힙은 동적으로 무작위로 할당되는 모든 메모리에 사용되는 일반적인 용어입니다. 즉, 순서가 맞지 않습니다. 메모리는 일반적으로 OS에 의해 할당되며 응용 프로그램은이 할당을 수행하기 위해 API 함수를 호출합니다. 동적으로 할당 된 메모리를 관리하는 데는 상당한 오버 헤드가 필요하며 일반적으로 사용되는 프로그래밍 언어 또는 환경의 런타임 코드에 의해 처리됩니다.
그들의 범위는 무엇입니까?
호출 스택은 프로그래밍의 의미에서 '범위'와 관련이없는 저수준 개념입니다. 일부 코드를 디스 어셈블하면 스택의 일부에 대한 상대 포인터 스타일 참조가 표시되지만 상위 수준 언어에 관한 한 언어는 자체 범위 규칙을 적용합니다. 그러나 스택의 중요한 측면 중 하나는 함수가 반환되면 해당 함수의 로컬 항목이 즉시 스택에서 해제된다는 것입니다. 프로그래밍 언어가 작동하는 방식을 고려할 때 예상했던 방식으로 작동합니다. 힙에서는 정의하기도 어렵습니다. 범위는 OS에 의해 노출되는 모든 것이지만 프로그래밍 언어는 아마도 응용 프로그램의 "범위"에 대한 규칙을 추가 할 것입니다. 프로세서 아키텍처와 OS는 프로세서가 물리적 주소로 변환하고 페이지 오류 등이있는 가상 주소 지정을 사용합니다. 그들은 어떤 페이지가 어떤 응용 프로그램에 속하는지 추적합니다. 그러나 프로그래밍 언어가 메모리를 할당하고 해제하는 데 사용하는 방법을 사용하고 오류를 확인하기 때문에 (어떤 이유로 든 할당 / 해제에 실패하면) 이에 대해 걱정할 필요가 없습니다.
각각의 크기를 결정하는 것은 무엇입니까?
다시 말하지만, 언어, 컴파일러, 운영 체제 및 아키텍처에 따라 다릅니다. 스택은 정의상 연속 메모리 여야하므로 일반적으로 사전 할당됩니다. 언어 컴파일러 또는 OS가 크기를 결정합니다. 스택에 엄청난 양의 데이터를 저장하지 않으므로 원치 않는 무한 재귀 (즉, "스택 오버플로") 또는 기타 비정상적인 프로그래밍 결정의 경우를 제외하고는 완전히 사용해서는 안 될만큼 충분히 커집니다.
힙은 동적으로 할당 할 수있는 모든 항목에 대한 일반적인 용어입니다. 어떤 방식으로 보느냐에 따라 끊임없이 크기가 변합니다. 최신 프로세서 및 운영 체제에서 작동하는 정확한 방식은 어쨌든 매우 추상화되어 있으므로 일반적으로 작동 방식에 대해 크게 걱정할 필요가 없습니다. 단, (사용할 수있는 언어에서는) 메모리를 사용하지 않아야합니다. 아직 할당하지 않았거나 해제 한 메모리.
더 빨리 만드는 것은 무엇입니까?
모든 여유 메모리가 항상 연속적이기 때문에 스택이 더 빠릅니다. 사용 가능한 메모리의 모든 세그먼트에 대한 목록을 유지할 필요가 없으며 스택의 현재 상단에 대한 단일 포인터 만 있으면됩니다. 컴파일러는 일반적으로이 목적을 위해 특수하고 빠른 레지스터에이 포인터를 저장합니다. 또한 스택에 대한 후속 작업은 일반적으로 매우 가까운 메모리 영역에 집중되어있어 매우 낮은 수준에서 온다이 프로세서의 최적화에 적합합니다.캐시.
|
(나는이 답변을 다소 속이는 다른 질문에서 옮겼습니다.)
질문에 대한 대답은 구현에 따라 다르며 컴파일러와 프로세서 아키텍처에 따라 다를 수 있습니다. 그러나 여기에 간단한 설명이 있습니다.
스택과 힙은 모두 기본 운영 체제에서 할당 된 메모리 영역입니다 (종종 요청시 실제 메모리에 매핑되는 가상 메모리).
다중 스레드 환경에서 각 스레드는 완전히 독립적 인 스택을 가지지 만 힙을 공유합니다. 동시 액세스는 힙에서 제어되어야하며 스택에서는 가능하지 않습니다.
힙
힙에는 사용 된 블록과 사용 가능한 블록의 링크 된 목록이 포함됩니다. 힙에 대한 새 할당 (new 또는 malloc에 ​​의해)은 사용 가능한 블록 중 하나에서 적절한 블록을 생성하여 충족됩니다. 이를 위해서는 힙의 블록 목록을 업데이트해야합니다. 힙의 블록에 대한이 메타 정보는 종종 모든 블록 바로 앞의 작은 영역에 힙에 저장됩니다.
힙이 커짐에 따라 새 블록은 종종 낮은 주소에서 높은 주소로 할당됩니다. 따라서 힙은 메모리가 할당됨에 따라 크기가 증가하는 메모리 블록의 힙으로 생각할 수 있습니다. 힙이 할당하기에 너무 작 으면 기본 운영 체제에서 더 많은 메모리를 확보하여 크기를 늘릴 수 있습니다.
많은 작은 블록을 할당하고 할당 해제하면 사용 된 블록 사이에 많은 작은 여유 블록이 산재 해있는 상태로 힙이 남을 수 있습니다. 사용 가능한 블록의 결합 된 크기가 충분히 클 수 있음에도 불구하고 사용 가능한 블록이 할당 요청을 충족 할만큼 충분히 크지 않기 때문에 큰 블록 할당 요청이 실패 할 수 있습니다. 이를 힙 조각화라고합니다.
사용 가능한 블록에 인접한 사용 된 블록이 할당 해제되면 새로운 사용 가능한 블록이 인접한 사용 가능한 블록과 병합되어 더 큰 사용 가능한 블록을 생성하여 힙의 조각화를 효과적으로 줄일 수 있습니다.
스택
스택은 종종 스택 포인터라는 CPU의 특수 레지스터와 밀접하게 연계되어 작동합니다. 처음에 스택 포인터는 스택의 맨 위 (스택에서 가장 높은 주소)를 가리 킵니다.
CPU에는 값을 스택에 푸시하고 스택에서 다시 팝하기위한 특별한 명령이 있습니다. 푸시 할 때마다 스택 포인터의 현재 위치에 값이 저장되고 스택 포인터가 감소합니다. 팝은 스택 포인터가 가리키는 값을 검색 한 다음 스택 포인터를 증가시킵니다 (스택에 값을 추가하면 스택 포인터가 감소하고 값을 제거하면 값이 증가한다는 사실에 혼동하지 마십시오. 바닥). 저장 및 검색된 값은 CPU 레지스터의 값입니다.
함수가 호출 될 때 CPU는 현재 명령어 포인터, 즉 스택에서 실행되는 코드의 주소를 푸시하는 특수 명령어를 사용합니다. 그런 다음 CPU는 다음을 설정하여 기능으로 이동합니다.
호출 된 함수의 주소에 대한 명령어 포인터. 나중에 함수가 반환되면 이전 명령어 포인터가 스택에서 팝되고 함수 호출 직후 코드에서 실행이 다시 시작됩니다.
함수가 입력되면 스택 포인터가 감소하여 로컬 (자동) 변수에 대해 스택에 더 많은 공간을 할당합니다. 함수에 하나의 로컬 32 비트 변수가 있으면 스택에 4 바이트가 따로 설정됩니다. 함수가 반환되면 스택 포인터가 다시 이동하여 할당 된 영역을 해제합니다.
함수에 매개 변수가 있으면 함수를 호출하기 전에 스택으로 푸시됩니다. 그러면 함수의 코드가 현재 스택 포인터에서 스택 위로 이동하여 이러한 값을 찾을 수 있습니다.
중첩 함수 호출은 매력처럼 작동합니다. 각각의 새로운 호출은 함수 매개 변수, 반환 주소 및 지역 변수에 대한 공간을 할당하며 이러한 활성화 레코드는 중첩 된 호출에 대해 스택 될 수 있으며 함수가 반환 될 때 올바른 방식으로 해제됩니다.
스택은 제한된 메모리 블록이므로 너무 많은 중첩 함수를 호출하거나 지역 변수에 너무 많은 공간을 할당하여 스택 오버플로를 일으킬 수 있습니다. 종종 스택에 사용되는 메모리 영역은 스택의 맨 아래 (가장 낮은 주소) 아래에 쓰면 CPU에서 트랩 또는 예외가 트리거되는 방식으로 설정됩니다. 이 예외적 인 조건은 런타임에 포착되어 일종의 스택 오버플로 예외로 변환 될 수 있습니다.
스택 대신 힙에 함수를 할당 할 수 있습니까?
아니요, 함수 (예 : 로컬 또는 자동 변수)에 대한 활성화 레코드는 이러한 변수를 저장할뿐만 아니라 중첩 된 함수 호출을 추적하는 데 사용되는 스택에 할당됩니다.
힙 관리 방법은 실제로 런타임 환경에 달려 있습니다. C는 malloc을 사용하고 C ++는 new를 사용하지만 다른 많은 언어에는 가비지 콜렉션이 있습니다.
그러나 스택은 프로세서 아키텍처와 밀접하게 연결된 더 낮은 수준의 기능입니다. 충분한 공간이 없을 때 힙을 늘리는 것은 그리 어렵지 않습니다.힙을 처리하는 라이브러리 호출에서 구현할 수 있습니다. 그러나 스택 오버플로는 너무 늦을 때만 발견되므로 스택을 늘리는 것은 종종 불가능합니다. 실행 스레드를 종료하는 것이 유일한 실행 가능한 옵션입니다.
|
다음 C # 코드에서
공개 무효 Method1 ()
{
int i = 4;
int y = 2;
class1 cls1 = 새로운 class1 ();
}
메모리를 관리하는 방법은 다음과 같습니다.
함수 호출이 스택에있는 동안 만 지속되어야하는 로컬 변수. 힙은 우리가 실제로 알지 못하지만 얼마 동안 지속될 것으로 예상되는 수명의 변수에 사용됩니다. 대부분의 언어에서 변수를 스택에 저장하려면 컴파일 타임에 변수의 크기를 아는 것이 중요합니다.
객체 (업데이트 할 때 크기가 달라짐)는 생성 시점에 얼마나 오래 지속 될지 모르기 때문에 힙에 있습니다. 많은 언어에서 힙은 더 이상 참조가없는 객체 (예 : cls1 객체)를 찾기 위해 가비지 수집됩니다.
Java에서 대부분의 개체는 힙으로 직접 이동합니다. C / C ++와 같은 언어에서는 포인터를 처리하지 않을 때 구조체와 클래스가 스택에 남아있을 수 있습니다.
자세한 내용은 여기에서 찾을 수 있습니다.
스택과 힙 메모리 할당의 차이점«timmurphy.org
그리고 여기:
스택 및 힙에 개체 만들기
이 기사는 위 그림의 출처입니다. 여섯 가지 중요한 .NET 개념 : 스택, 힙, 값 유형, 참조 유형, boxing 및 unboxing-CodeProject
그러나 일부 부정확성이 포함될 수 있습니다.
|
스택
함수를 호출하면 해당 함수에 대한 인수와 다른 오버 헤드가 스택에 추가됩니다. 반환 할 위치와 같은 일부 정보도 여기에 저장됩니다.
함수 내에서 변수를 선언하면 해당 변수도 스택에 할당됩니다.
항상 할당 한 순서의 역순으로 할당을 해제하므로 스택 할당 해제는 매우 간단합니다. 함수를 입력하면 스택 항목이 추가되고 종료 할 때 해당 데이터가 제거됩니다. 이는 다른 많은 함수를 호출하는 많은 함수를 호출하지 않는 한 (또는 재귀 적 솔루션을 생성하지 않는 한) 스택의 작은 영역 내에 머무르는 경향이 있음을 의미합니다.
힙
힙은 즉석에서 생성 한 데이터를 넣는 위치에 대한 일반적인 이름입니다. 프로그램에서 생성 할 우주선의 수를 모르는 경우 new (또는 malloc 또는 이와 동등한) 연산자를 사용하여 각 우주선을 만들 수 있습니다. 이 할당은 잠시 동안 유지 될 것이므로 우리가 만든 것과 다른 순서로 물건을 해제 할 가능성이 높습니다.
따라서 힙은 훨씬 더 복잡합니다. 사용되지 않은 메모리 영역이 청크와 인터리브되어 메모리가 조각화되기 때문입니다. 필요한 크기의 여유 메모리를 찾는 것은 어려운 문제입니다. 이것이 힙을 피해야하는 이유입니다 (아직 자주 사용되지만).
이행
스택과 힙의 구현은 일반적으로 런타임 / OS에 달려 있습니다. 종종 성능에 중요한 게임 및 기타 응용 프로그램은 힙에서 많은 양의 메모리를 가져 와서 메모리를 위해 OS에 의존하지 않도록 내부적으로 처리하는 자체 메모리 솔루션을 만듭니다.
이것은 메모리 사용량이 표준과 상당히 다른 경우에만 실용적입니다. 즉, 하나의 대규모 작업에서 레벨을로드하고 다른 대규모 작업에서 전체를 뽑을 수있는 게임의 경우에만 실용적입니다.
메모리의 물리적 위치
프로그램이 물리적 데이터가 다른 곳에있는 특정 주소 (하드 디스크에서도!)에 액세스 할 수 있다고 생각하게 만드는 가상 메모리라는 기술 때문에 이것은 생각보다 덜 관련성이 있습니다. 스택에 대해 얻는 주소는 호출 트리가 깊어짐에 따라 증가하는 순서입니다. 힙의 주소는 예측할 수 없으며 (즉, 특정 함축) 솔직히 중요하지 않습니다.
|
명확히하기 위해이 답변에는 잘못된 정보가 있습니다 (토마스는 댓글 후 답변을 수정했습니다, 멋진 :)). 다른 답변은 정적 할당의 의미를 설명하지 않습니다. 따라서 세 가지 주요 할당 형식과 이들이 일반적으로 힙, 스택 및 데이터 세그먼트와 어떻게 관련되는지 아래에서 설명하겠습니다. 또한 사람들이 이해하는 데 도움이되도록 C / C ++와 Python으로 몇 가지 예를 보여 드리겠습니다.
"정적"(일명 정적으로 할당 된) 변수는 스택에 할당되지 않습니다. 그렇게 가정하지 마십시오. 많은 사람들이 "정적"이 "스택"처럼 들리기 때문에 그렇게합니다. 실제로 스택이나 힙에 존재하지 않습니다. 데이터 세그먼트라고하는 부분입니다.
그러나 일반적으로 "스택"및 "힙"보다는 "범위"및 "수명"을 고려하는 것이 좋습니다.
범위는 코드에서 변수에 액세스 할 수있는 부분을 나타냅니다. 일반적으로 우리는 범위가 훨씬 더 복잡해질 수 있지만 로컬 범위 (현재 함수에서만 액세스 할 수 있음) 대 전역 범위 (어디서나 액세스 할 수 있음)를 생각합니다.
수명은 프로그램 실행 중에 변수가 할당되고 할당 해제되는시기를 나타냅니다. 일반적으로 정적 할당 (변수프로그램의 전체 기간 동안 지속되므로 여러 함수 호출에서 동일한 정보를 저장하는 데 유용합니다. 함수를 사용하고 완료되면 버릴 수 있음) 대 동적 할당 (정적 또는 자동과 같은 컴파일 시간 대신 런타임에 기간이 정의 된 변수).
대부분의 컴파일러와 인터프리터는 스택, 힙 등을 사용하는 측면에서이 동작을 유사하게 구현하지만 컴파일러는 동작이 올바른 한 원하는 경우 때때로 이러한 규칙을 위반할 수 있습니다. 예를 들어, 최적화로 인해 로컬 변수는 대부분의 로컬 변수가 스택에 존재하더라도 레지스터에만 존재하거나 완전히 제거 될 수 있습니다. 몇 가지 의견에서 지적했듯이 스택이나 힙을 사용하지 않고 대신 다른 저장 메커니즘을 사용하는 컴파일러를 자유롭게 구현할 수 있습니다 (스택과 힙이 이에 적합하기 때문에 거의 수행되지 않음).
이 모든 것을 설명하기 위해 간단한 주석이 달린 C 코드를 제공하겠습니다. 배우는 가장 좋은 방법은 디버거에서 프로그램을 실행하고 동작을 관찰하는 것입니다. 파이썬을 읽고 싶다면 대답의 끝으로 건너 뛰십시오 :)
// 프로그램 / DLL이 처음로드 될 때 데이터 세그먼트에 정적으로 할당됩니다.
// 프로그램 / DLL이 종료되면 할당 해제 됨
// 범위-코드의 어느 곳에서나 액세스 가능
int someGlobalVariable;
// 프로그램이 처음로드 될 때 데이터 세그먼트에 정적으로 할당 됨
// 프로그램 / DLL이 종료되면 할당 해제 됨
// 범위-이 특정 코드 파일의 어디에서나 액세스 할 수 있습니다.
static int someStaticVariable;
// MyFunction이 호출 될 때마다 "someArgument"가 스택에 할당됩니다.
// MyFunction이 반환하면 "someArgument"가 할당 해제됩니다.
// 범위-MyFunction () 내에서만 액세스 할 수 있습니다.
void MyFunction (int someArgument) {
// 프로그램이 처음로드 될 때 데이터 세그먼트에 정적으로 할당 됨
// 프로그램 / DLL이 종료되면 할당 해제 됨
// 범위-MyFunction () 내에서만 액세스 할 수 있습니다.
static int someLocalStaticVariable;
// MyFunction이 호출 될 때마다 스택에 할당됩니다.
// MyFunction이 반환 될 때 할당 해제 됨
// 범위-MyFunction () 내에서만 액세스 할 수 있습니다.
int someLocalVariable;
// MyFunction이 호출 될 때마다 스택에 * pointer *가 할당됩니다.
//이 포인터는 MyFunction이 반환 할 때 할당 해제됩니다.
// 범위-포인터는 MyFunction () 내에서만 액세스 할 수 있습니다.
int * someDynamicVariable;
//이 줄은 힙에 정수를위한 공간을 할당합니다.
//이 라인이 실행될 때. 이것은 시작 부분이 아닙니다.
// 자동 변수와 같은 MyFunction () 호출
// 범위-MyFunction () 내의 코드 만이 공간에 액세스 할 수 있습니다.
// *이 특정 변수를 통해 *.
// 그러나 주소를 다른 곳에 전달하면 해당 코드
// 액세스 가능
someDynamicVariable = 새로운 int;
//이 줄은 힙의 정수 공간을 할당 해제합니다.
// 작성하지 않으면 메모리가 "누수"됩니다.
// 스택과 힙의 근본적인 차이점에 유의하십시오.
// 힙을 관리해야합니다. 스택은 우리를 위해 관리됩니다.
someDynamicVariable을 삭제하십시오.
// 다른 경우에는이 힙 공간을 할당 해제하는 대신
// 나중에 사용하기 위해 주소를 더 영구적 인 위치에 저장할 수 있습니다.
// 일부 언어는 할당 해제도 처리하지만 ...
// 항상 어떤 메커니즘에 의해 런타임에 처리되어야합니다.
// 함수가 반환되면 someArgument, someLocalVariable
// 포인터 someDynamicVariable이 할당 해제됩니다.
// someDynamicVariable이 가리키는 공간은 이미
// 반환하기 전에 할당 해제됩니다.
반환;
}
// someGlobalVariable, someStaticVariable 및
// someLocalStaticVariable은 계속 존재하지만
// 프로그램이 종료 될 때까지 할당 해제됩니다.
수명과 범위를 구별하는 것이 중요한 이유에 대한 특히 가슴 아픈 예는 변수가 로컬 범위를 가질 수 있지만 정적 수명을 가질 수 있다는 것입니다 (예 : 위 코드 샘플의 "someLocalStaticVariable"). 이러한 변수는 일반적이지만 비공식적 인 명명 습관을 매우 혼란스럽게 만들 수 있습니다. 예를 들어 "로컬"이라고 말하면 일반적으로 "로컬 범위의 자동 할당 변수"를 의미하고 글로벌이라고하면 일반적으로 "글로벌 범위의 정적으로 할당 된 변수"를 의미합니다. 불행히도 "파일 범위가 정적으로 할당 된 변수"와 같은 경우에는 많은 사람들이 "응 ???"이라고 말합니다.
C / C ++의 일부 구문 선택은이 문제를 악화시킵니다. 예를 들어 많은 사람들은 아래에 표시된 구문 때문에 전역 변수가 "정적"이 아니라고 생각합니다.
int var1; // 전역 범위와 정적 할당이 있습니다.
static int var2; // 파일 범위와 정적 할당이 있습니다.
int main () {return 0;}
위의 선언에 "static"키워드를 넣으면 var2가 전역 범위를 갖지 못합니다. 그럼에도 불구하고 전역 var1에는 정적 할당이 있습니다. 이것은 아니다직관적! 따라서 범위를 설명 할 때 "정적"이라는 단어를 사용하지 않고 대신 "파일"또는 "파일 제한"범위와 같은 것을 말하려고합니다. 그러나 많은 사람들이 "정적"또는 "정적 범위"라는 문구를 사용하여 하나의 코드 파일에서만 액세스 할 수있는 변수를 설명합니다. 수명과 관련하여 "정적"은 항상 변수가 프로그램 시작시 할당되고 프로그램이 종료 될 때 할당 해제됨을 의미합니다.
어떤 사람들은 이러한 개념을 C / C ++에만 해당한다고 생각합니다. 그렇지 않습니다. 예를 들어, 아래의 Python 샘플은 세 가지 유형의 할당을 모두 보여줍니다 (여기서는 다루지 않겠지 만 해석 언어에는 미묘한 차이가있을 수 있습니다).
datetime 가져 오기 datetime에서
클래스 동물 :
_FavoriteFood = 'Undefined'# _FavoriteFood는 정적으로 할당됩니다.
def PetAnimal (self) :
curTime = datetime.time (datetime.now ()) # curTime이 자동으로 할당됩니다.
print ( "애무 해주셔서 감사합니다.하지만"+ str (curTime) + "입니다. 먹여주세요. 제가 가장 좋아하는 음식은"+ self._FavoriteFood)
class Cat (Animal) :
_FavoriteFood = 'tuna'# 우리가 재정의했기 때문에 Cat 클래스에는 Animal의 것과는 다른 정적으로 할당 된 _FavoriteFood 변수가 있습니다.
클래스 Dog (Animal) :
_FavoriteFood = 'steak'# 마찬가지로 Dog 클래스는 자체 정적 변수를 가져옵니다. 주의 할 점은이 하나의 정적 변수는 Dog의 모든 인스턴스간에 공유되므로 동적이 아닙니다!
__name__ == "__main__"인 경우 :
whiskers = Cat () # 동적 할당
fido = Dog () # 동적 할당
rinTinTin = Dog () # 동적 할당
whiskers.PetAnimal ()
fido.PetAnimal ()
rinTinTin.PetAnimal ()
Dog._FavoriteFood = '젖뼈'
whiskers.PetAnimal ()
fido.PetAnimal ()
rinTinTin.PetAnimal ()
# 출력은 다음과 같습니다.
# 귀여워 줘서 고마워. 하지만 그것은 13 : 05 : 02.255000입니다. 내가 제일 좋아하는 음식은 참치
# 귀여워 줘서 고마워. 하지만 그것은 13 : 05 : 02.255000입니다. 내가 제일 좋아하는 음식은 스테이크
# 귀여워 줘서 고마워. 그러나 그것은 13 : 05 : 02.255000입니다. 내가 제일 좋아하는 음식은 스테이크
# 귀여워 줘서 고마워. 하지만 그것은 13 : 05 : 02.255000입니다. 내가 제일 좋아하는 음식은 참치
# 귀여워 줘서 고마워. 하지만 그것은 13 : 05 : 02.255000입니다. 내가 제일 좋아하는 음식은 밀크 본
# 귀여워 줘서 고마워. 하지만 그것은 13 : 05 : 02.256000입니다. 내가 제일 좋아하는 음식은 밀크 본
|
다른 사람들은 넓은 획에 대해 꽤 잘 대답 했으므로 몇 가지 세부 사항을 던질 것입니다.
스택과 힙은 단수 일 필요가 없습니다. 둘 이상의 스택이있는 일반적인 상황은 프로세스에 둘 이상의 스레드가있는 경우입니다. 이 경우 각 스레드에는 자체 스택이 있습니다. 하나 이상의 힙을 가질 수도 있습니다. 예를 들어 일부 DLL 구성으로 인해 다른 힙에서 다른 DLL이 할당 될 수 있으므로 일반적으로 다른 라이브러리에서 할당 한 메모리를 해제하는 것이 좋지 않습니다.
C에서는 힙에 할당하는 alloc과 달리 스택에 할당하는 alloca를 사용하여 가변 길이 할당의 이점을 얻을 수 있습니다. 이 메모리는 return 문에서 살아남지 못하지만 스크래치 버퍼에 유용합니다.
Windows에서 많이 사용하지 않는 거대한 임시 버퍼를 만드는 것은 무료가 아닙니다. 이는 컴파일러가 스택이 존재하는지 확인하기 위해 함수를 입력 할 때마다 호출되는 스택 프로브 루프를 생성하기 때문입니다 (Windows는 스택을 확장해야하는시기를 감지하기 위해 스택 끝에 단일 가드 페이지를 사용하기 때문입니다. 스택 끝에서 한 페이지 이상 메모리에 액세스하면 충돌이 발생합니다). 예:
무효 myfunction ()
{
char big [10000000];
// 99 %의 시간 중 처음 1K 만 사용하는 작업을 수행합니다.
}
|
다른 사람들이 귀하의 질문에 직접 답변했지만 스택과 힙을 이해하려고 할 때 스레드 및 mmap () 기반 할당자가없는 기존 UNIX 프로세스의 메모리 레이아웃을 고려하는 것이 유용하다고 생각합니다. 메모리 관리 용어집 웹 페이지에는이 메모리 레이아웃의 다이어그램이 있습니다.
스택과 힙은 일반적으로 프로세스 가상 주소 공간의 반대쪽 끝에 있습니다. 스택은 액세스 할 때 커널에서 설정 한 크기까지 자동으로 증가합니다 (setrlimit (RLIMIT_STACK, ...)로 조정할 수 있음). 힙은 메모리 할당자가 brk () 또는 sbrk () 시스템 호출을 호출 할 때 증가하여 더 많은 물리적 메모리 페이지를 프로세스의 가상 주소 공간에 매핑합니다.
일부 임베디드 시스템과 같이 가상 메모리가없는 시스템에서는 스택과 힙이 크기가 고정 된 것을 제외하고 동일한 기본 레이아웃이 적용되는 경우가 많습니다. 그러나 다른 임베디드 시스템 (예 : Microchip PIC 마이크로 컨트롤러에 기반한 시스템)에서 프로그램 스택은 데이터 이동 명령으로 주소를 지정할 수없는 별도의 메모리 블록이며 프로그램 흐름 명령 (호출, 반환 등). Intel Itanium 프로세서와 같은 다른 아키텍처에는 다중 스택이 있습니다. 이러한 의미에서 스택은 CPU 아키텍처의 요소입니다.
|
스택은 일부입니다'pop'(스택에서 값 제거 및 반환) 및 'push'(스택에 값 푸시)와 같은 몇 가지 주요 어셈블리 언어 명령을 통해 조작 할 수있는 메모리의 일부뿐 아니라 호출 (서브 루틴 호출-this 스택으로 돌아 가기 위해 주소를 푸시하고 반환합니다 (서브 루틴에서 리턴-스택에서 주소를 꺼내서 점프). 스택 포인터 레지스터 아래의 메모리 영역이며 필요에 따라 설정할 수 있습니다. 스택은 서브 루틴에 인수를 전달하고 서브 루틴을 호출하기 전에 레지스터의 값을 보존하는데도 사용됩니다.
힙은 일반적으로 malloc과 같은 시스템 호출을 통해 운영 체제에서 애플리케이션에 제공하는 메모리의 일부입니다. 최신 OS에서이 메모리는 호출 프로세스 만 액세스 할 수있는 페이지 집합입니다.
스택의 크기는 런타임에 결정되며 일반적으로 프로그램이 시작된 후에는 커지지 않습니다. C 프로그램에서 스택은 각 함수 내에서 선언 된 모든 변수를 보유 할 수있을만큼 충분히 커야합니다. 힙은 필요에 따라 동적으로 증가하지만 OS는 궁극적으로 호출을 수행합니다 (보통 malloc에서 요청한 값보다 더 많이 힙을 증가 시키므로 적어도 향후 malloc이 커널로 돌아가서 더 많은 메모리 확보.이 동작은 종종 사용자 정의 할 수 있습니다.)
프로그램을 시작하기 전에 스택을 할당했기 때문에 스택을 사용하기 전에 malloc 할 필요가 없으므로 약간의 이점이 있습니다. 실제로 페이지가 구현되고 저장되는 위치는 구현 세부 사항이기 때문에 가상 메모리 하위 시스템이있는 최신 운영 체제에서 무엇이 빠르고 느려질 지 예측하기가 매우 어렵습니다.
|
스택이란 무엇입니까?
스택은 일반적으로 깔끔하게 배열 된 객체 더미입니다.
컴퓨팅 아키텍처의 스택은 후입 선출 방식으로 데이터가 추가되거나 제거되는 메모리 영역입니다.
다중 스레드 응용 프로그램에서 각 스레드는 자체 스택을 갖습니다.
힙이란 무엇입니까?
힙은 아무렇게나 쌓인 물건의 어수선한 모음입니다.
컴퓨팅 아키텍처에서 힙은 운영 체제 또는 메모리 관리자 라이브러리에 의해 자동으로 관리되는 동적 할당 메모리 영역입니다.
힙의 메모리는 프로그램 실행 중에 정기적으로 할당, 할당 해제 및 크기가 조정되며 이로 인해 조각화라는 문제가 발생할 수 있습니다.
조각화는 메모리 개체가 추가 메모리 개체를 보유하기에 너무 작은 사이에 작은 공간이 할당 될 때 발생합니다.
최종 결과는 추가 메모리 할당에 사용할 수없는 힙 공간의 백분율입니다.
양자
다중 스레드 응용 프로그램에서 각 스레드는 자체 스택을 갖습니다. 그러나 모든 다른 스레드는 힙을 공유합니다.
서로 다른 스레드가 다중 스레드 응용 프로그램에서 힙을 공유하기 때문에 스레드간에 약간의 조정이 있어야합니다. 그래야 스레드가 힙에있는 동일한 메모리 부분에 액세스하고 조작하지 않도록합니다. 동시.
어느 것이 더 빠릅니까? 스택 또는 힙? 그리고 왜?
스택은 힙보다 훨씬 빠릅니다.
이는 메모리가 스택에 할당되는 방식 때문입니다.
스택에 메모리를 할당하는 것은 스택 포인터를 위로 이동하는 것만 큼 간단합니다.
프로그래밍을 처음 접하는 사람들에게는 스택이 더 쉽기 때문에 사용하는 것이 좋습니다.
스택이 작기 때문에 데이터에 필요한 메모리 양을 정확히 알고 있거나 데이터 크기가 매우 작다는 것을 알고있는 경우 사용하고 싶을 것입니다.
데이터에 많은 메모리가 필요하다는 것을 알고 있거나 동적 배열과 같이 필요한 메모리 양이 확실하지 않을 때 힙을 사용하는 것이 좋습니다.
자바 메모리 모델
스택은 로컬 변수 (메서드 매개 변수 포함)가 저장되는 메모리 영역입니다. 개체 변수에 관해서는 힙의 실제 개체에 대한 참조 (포인터) 일뿐입니다.
개체가 인스턴스화 될 때마다 해당 개체의 데이터 (상태)를 보관하기 위해 힙 메모리 청크가 따로 설정됩니다. 개체는 다른 개체를 포함 할 수 있으므로이 데이터 중 일부는 실제로 중첩 된 개체에 대한 참조를 보유 할 수 있습니다.
|
다른 많은 분들이이 문제에 대해 대부분 정답을 주셨다 고 생각합니다.
그러나 놓친 한 가지 세부 사항은 "힙"이 실제로 "무료 상점"이라고 불려야한다는 것입니다. 이러한 구분의 이유는 원래의 무료 저장소가 "이항 힙"이라는 데이터 구조로 구현 되었기 때문입니다. 이러한 이유로 malloc () / free ()의 초기 구현에서 할당하는 것은 힙에서 할당하는 것입니다. 그러나 오늘날 대부분의 무료 상점은 이항 힙이 아닌 매우 정교한 데이터 구조로 구현됩니다.
|
스택으로 몇 가지 흥미로운 일을 할 수 있습니다. 예를 들어, alloca와 같은 기능이 있습니다 (사용에 관한 방대한 경고를 통과 할 수 있다고 가정). malloc의 한 형태입니다.특히 메모리에 힙이 아닌 스택을 사용합니다.
즉, 스택 기반 메모리 오류는 내가 경험 한 최악의 일부입니다. 힙 메모리를 사용하고 할당 된 블록의 경계를 초과하면 세그먼트 오류를 ​​유발할 수있는 적절한 기회가 있습니다. (100 %가 아님 : 블록은 이전에 할당 한 다른 블록과 우연히 인접 할 수 있습니다.) 그러나 스택에 생성 된 변수는 항상 서로 인접하므로 경계를 벗어난 쓰기는 다른 변수의 값을 변경할 수 있습니다. 나는 내 프로그램이 논리의 법칙을 따르지 않는다고 느낄 때마다 아마도 버퍼 오버 플로라는 것을 배웠다.
|
간단히 말해 스택은 지역 변수가 생성되는 곳입니다. 또한 서브 루틴을 호출 할 때마다 프로그램 카운터 (다음 기계 명령어에 대한 포인터)와 모든 중요한 레지스터가 있으며 때로는 매개 변수가 스택에 푸시됩니다. 그런 다음 서브 루틴 내부의 모든 지역 변수가 스택으로 푸시되고 거기에서 사용됩니다. 서브 루틴이 완료되면 모든 것이 스택에서 다시 튀어 나옵니다. PC와 레지스터 데이터는 튀어 나온대로 가져 와서 원래 위치로 되돌 리므로 프로그램이 즐거운 방식으로 진행될 수 있습니다.
힙은 동적 메모리 할당이 만들어지는 메모리 영역입니다 (명시 적 "새로 만들기"또는 "할당"호출). 다양한 크기의 메모리 블록과 할당 상태를 추적 할 수있는 특수 데이터 구조입니다.
"클래식"시스템에서 RAM은 스택 포인터가 메모리 맨 아래에서 시작하고 힙 포인터가 맨 위에서 시작하여 서로를 향해 성장하도록 배치되었습니다. 겹치면 RAM이 부족한 것입니다. 하지만 최신 멀티 스레드 OS에서는 작동하지 않습니다. 모든 스레드에는 자체 스택이 있어야하며 동적으로 생성 될 수 있습니다.
|
WikiAnwser에서.
스택
함수 나 메서드가 차례로 다른 함수를 호출하는 다른 함수를 호출하면 마지막 함수가 해당 값을 반환 할 때까지 모든 함수의 실행이 일시 중단 된 상태로 유지됩니다.
이 일시 중단 된 함수 호출 체인은 스택의 요소 (함수 호출)가 서로 의존하기 때문에 스택입니다.
스택은 예외 처리 및 스레드 실행에서 고려해야 할 중요합니다.
더미
힙은 단순히 프로그램에서 변수를 저장하는 데 사용하는 메모리입니다.
힙의 요소 (변수)는 서로 종속성이 없으며 언제든지 무작위로 액세스 할 수 있습니다.
|
스택
매우 빠른 액세스
명시 적으로 변수 할당을 해제 할 필요가 없습니다.
공간은 CPU에 의해 효율적으로 관리되며 메모리는 조각화되지 않습니다.
지역 변수 만
스택 크기 제한 (OS에 따라 다름)
변수의 크기를 조정할 수 없습니다.
더미
변수는 전역 적으로 액세스 할 수 있습니다.
메모리 크기 제한 없음
(비교적) 느린 액세스
공간의 효율적인 사용이 보장되지 않으며, 메모리 블록이 할당 된 후 해제됨에 따라 시간이 지남에 따라 메모리가 조각화 될 수 있습니다.
메모리를 관리해야합니다 (변수 할당 및 해제를 담당합니다).
realloc ()을 사용하여 변수의 크기를 조정할 수 있습니다.
|
간단히
스택은 정적 메모리 할당에 사용되고 동적 메모리 할당에는 힙이 사용되며 둘 다 컴퓨터의 RAM에 저장됩니다.
상세히
스택
스택은 CPU에 의해 매우 밀접하게 관리되고 최적화되는 "LIFO"(후입 선출) 데이터 구조입니다. 함수가 새 변수를 선언 할 때마다 스택에 "푸시"됩니다. 그런 다음 함수가 종료 될 때마다 해당 함수에 의해 스택에 푸시 된 모든 변수가 해제됩니다 (즉, 삭제됨). 스택 변수가 해제되면 해당 메모리 영역을 다른 스택 변수에 사용할 수 있습니다.
스택을 사용하여 변수를 저장할 때의 장점은 메모리가 자동으로 관리된다는 것입니다. 메모리를 직접 할당하거나 더 이상 필요하지 않으면 해제 할 필요가 없습니다. 또한 CPU가 스택 메모리를 매우 효율적으로 구성하기 때문에 스택 변수를 읽고 쓰는 것이 매우 빠릅니다.
더 많은 정보는 여기에서 찾을 수 있습니다.
힙
힙은 자동으로 관리되지 않는 컴퓨터 메모리 영역이며 CPU에서 엄격하게 관리하지 않습니다. 더 자유롭게 떠 다니는 메모리 영역이며 더 큽니다. 힙에 메모리를 할당하려면 내장 C 함수 인 malloc () 또는 calloc ()을 사용해야합니다. 힙에 메모리를 할당 한 후에는 더 이상 필요하지 않으면 free ()를 사용하여 해당 메모리를 할당 해제해야합니다.
이렇게하지 않으면 프로그램에 메모리 누수가 발생합니다. 즉, 힙의 메모리는 여전히 따로 보관되며 다른 프로세스에서 사용할 수 없습니다. 디버깅 섹션에서 볼 수 있듯이 메모리 누수를 감지하는 데 도움이되는 Valgrind라는 도구가 있습니다.
스택과 달리 힙에는 가변 크기에 대한 크기 제한이 없습니다 (컴퓨터의 명백한 물리적 제한 사항 제외). 힙 메모리는 포인터를 사용하여 힙의 메모리에 액세스해야하기 때문에 읽기 및 쓰기 속도가 약간 느립니다. 우리는 곧 포인터에 대해 이야기 할 것입니다.
스택과 달리힙에 생성 된 변수는 프로그램의 모든 기능에서 액세스 할 수 있습니다. 힙 변수는 본질적으로 범위가 전역입니다.
더 많은 정보는 여기에서 찾을 수 있습니다.
스택에 할당 된 변수는 메모리에 직접 저장되며이 메모리에 대한 액세스는 매우 빠르며 프로그램이 컴파일 될 때 할당이 처리됩니다. 함수 나 메서드가 차례로 다른 함수를 호출하는 다른 함수를 호출하면 마지막 함수가 해당 값을 반환 할 때까지 모든 함수의 실행이 일시 중단 된 상태로 유지됩니다. 스택은 항상 LIFO 순서로 예약되고 가장 최근에 예약 된 블록은 항상 해제 될 다음 블록입니다. 이렇게하면 스택을 추적하는 것이 정말 간단 해집니다. 스택에서 블록을 해제하는 것은 하나의 포인터를 조정하는 것 이상입니다.
힙에 할당 된 변수는 런타임에 할당 된 메모리를 가지며이 메모리에 액세스하는 것은 약간 느리지 만 힙 크기는 가상 메모리 크기에 의해서만 제한됩니다. 힙의 요소는 서로 종속성이 없으며 언제든지 무작위로 액세스 할 수 있습니다. 언제든지 블록을 할당하고 해제 할 수 있습니다. 이로 인해 주어진 시간에 힙의 어떤 부분이 할당되었는지 또는 해제되었는지 추적하는 것이 훨씬 더 복잡해집니다.
컴파일 시간 전에 할당해야하는 데이터의 양을 정확히 알고 있고 너무 크지 않은 경우 스택을 사용할 수 있습니다. 런타임에 필요한 데이터의 양을 정확히 모르거나 많은 데이터를 할당해야하는 경우 힙을 사용할 수 있습니다.
다중 스레드 상황에서 각 스레드는 완전히 독립적 인 스택을 가지지 만 힙을 공유합니다. 스택은 스레드별로 다르며 힙은 애플리케이션별로 다릅니다. 스택은 예외 처리 및 스레드 실행에서 고려해야 할 중요합니다.
각 스레드는 스택을 가져 오지만 일반적으로 애플리케이션에 대해 하나의 힙만 있습니다 (다양한 유형의 할당에 대해 여러 힙을 갖는 것은 드문 일이 아닙니다).
런타임에 응용 프로그램에 더 많은 힙이 필요하면 사용 가능한 메모리에서 메모리를 할당 할 수 있고 스택에 메모리가 필요한 경우 응용 프로그램에 할당 된 사용 가능한 메모리에서 메모리를 할당 할 수 있습니다.
심지어 여기와 여기에 더 자세한 내용이 나와 있습니다.
이제 질문에 대한 답을 찾으십시오.
OS 또는 언어 런타임에 의해 어느 정도까지 제어됩니까?
OS는 스레드가 생성 될 때 각 시스템 수준 스레드에 대해 스택을 할당합니다. 일반적으로 OS는 응용 프로그램에 대한 힙을 할당하기 위해 언어 런타임에서 호출됩니다.
더 많은 정보는 여기에서 찾을 수 있습니다.
그들의 범위는 무엇입니까?
이미 상단에 주어졌습니다.
"컴파일 시간 전에 할당해야하는 데이터의 양을 정확히 알고 있고 너무 크지 않은 경우 스택을 사용할 수 있습니다. 런타임에 필요한 데이터의 양을 정확히 알지 못하거나 다음과 같은 경우 힙을 사용할 수 있습니다. 많은 데이터를 할당해야합니다. "
여기에서 더 많은 것을 찾을 수 있습니다.
각각의 크기를 결정하는 것은 무엇입니까?
스택의 크기는 스레드가 생성 될 때 OS에 의해 설정됩니다. 힙의 크기는 애플리케이션 시작시 설정되지만 공간이 필요하면 증가 할 수 있습니다 (할당자가 운영 체제에서 더 많은 메모리를 요청 함).
더 빨리 만드는 것은 무엇입니까?
스택 할당은 실제로 스택 포인터를 이동하는 것이므로 훨씬 빠릅니다. 메모리 풀을 사용하면 힙 할당에서 비슷한 성능을 얻을 수 있지만 약간의 복잡성과 그 자체로 골칫거리가 있습니다.
또한 스택 대 힙은 성능 고려 사항 일뿐만 아니라 또한 개체의 예상 수명에 대해 많은 정보를 제공합니다.
자세한 내용은 여기에서 확인할 수 있습니다.
|
OK, 간단히 말해서, 그들은 주문 된 것과 주문되지 않은 것을 의미합니다 ...!
스택 : 스택 항목에서는 항목이 서로 겹쳐 지므로 처리하는 데 더 빠르고 효율적입니다! ...
따라서 항상 특정 항목을 가리키는 인덱스가 있으며 처리 속도도 빨라지고 항목 간의 관계도 있습니다! ...
힙 : 순서 없음, 처리 속도가 느려지고 특정 순서 나 인덱스없이 값이 함께 엉망이됩니다 ... 무작위가 있고 그들 사이에 관계가 없습니다 ... 따라서 실행 및 사용 시간이 다를 수 있습니다 ...
또한 어떻게 보이는지 보여주기 위해 아래 이미지를 만듭니다.
|
가상 메모리에있는 각 프로세스의 스택, 힙 및 데이터 :
|
1980 년대에 UNIX는 대기업이 자체적으로 운영하면서 토끼처럼 전파되었습니다.
Exxon은 역사에 잃어버린 수십 개의 브랜드 이름과 마찬가지로 하나를 가졌습니다.
메모리 배치 방법은 많은 구현 자의 재량에 달려 있습니다.
전형적인 C 프로그램은 다음과 같이 메모리에 평평하게 배치되었습니다.
brk () 값을 변경하여 증가 할 수있는 기회.
일반적으로 HEAP는이 brk 값 바로 아래였습니다.
brk를 ​​늘리면 사용 가능한 힙의 양이 늘어났습니다.
단일 스택은 일반적으로 메모리 영역 인 HEAP 아래 영역이었습니다.
다음 고정 메모리 블록의 맨 위까지 가치가 없습니다.
이 다음 블록은 종종 스택 데이터로 덮어 쓸 수있는 CODE였습니다.
그 시대의 유명한 해킹 중 하나에서.
일반적인 메모리 블록 중 하나는 BSS (제로 블록값)
실수로 한 제조업체의 제품에서 제로화되지 않았습니다.
다른 하나는 문자열과 숫자를 포함하여 초기화 된 값을 포함하는 DATA입니다.
세 번째는 CRT (C 런타임), 메인, 함수 및 라이브러리를 포함하는 CODE였습니다.
UNIX에서 가상 메모리의 출현으로 많은 제약이 바뀝니다.
이 블록이 연속적 일 필요가있는 객관적인 이유는 없습니다.
또는 크기가 고정되거나 지금 특정 방식으로 주문되었습니다.
물론, UNIX 이전에는 이러한 제약을받지 않는 Multics가있었습니다.
다음은 그 시대의 메모리 레이아웃 중 하나를 보여주는 회로도입니다.
|
몇 센트 : 메모리를 그래픽으로 그리고 더 간단하게 그리는 것이 좋을 것 같습니다.
화살표-스택 및 힙 증가, 프로세스 스택 크기 제한, OS에서 정의 됨, 스레드 생성 API의 매개 변수에 의해 스레드 스택 크기 제한이 일반적으로 표시됩니다. 힙은 일반적으로 프로세스 최대 가상 메모리 크기 (예 : 32 비트 2-4GB)로 제한됩니다.
매우 간단한 방법 : 프로세스 힙은 malloc ()과 같은 일반적인 경우에 메모리 할당에 사용하여 프로세스 및 내부의 모든 스레드에 대해 일반적입니다.
스택은 함수 호출, 로컬 함수 변수의 매개 변수로 처리되는 일반적인 경우 함수 반환 포인터 및 변수에 저장하기위한 빠른 메모리입니다.
|
일부 답변이 꼼꼼하게 진행되었으므로 진드기에 기여할 것입니다.
놀랍게도, 여러 (즉, 실행중인 OS 수준 스레드의 수와 관련이 없음) 호출 스택이 이국적인 언어 (PostScript) 또는 플랫폼 (Intel Itanium)뿐만 아니라 섬유, 녹색 스레드에서도 발견 될 것이라고 아무도 언급하지 않았습니다. 그리고 코 루틴의 일부 구현.
섬유, 녹색 스레드 및 코 루틴은 여러면에서 유사하므로 많은 혼란을 초래합니다. 섬유와 녹색 실의 차이점은 전자는 협력 멀티 태스킹을 사용하는 반면 후자는 협력 또는 선점 형 (또는 둘 다)을 특징으로 할 수 있다는 것입니다. 파이버와 코 루틴의 차이점은 여기를 참조하세요.
어쨌든 파이버, 녹색 스레드 및 코 루틴 모두의 목적은 단일 OS 수준 스레드 내에서 동시에 실행되는 여러 기능을 가지고 있지만 (구분에 대해서는이 SO 질문 참조) 서로간에 제어를 전달합니다. 조직화 된 방식으로.
파이버, 녹색 스레드 또는 코 루틴을 사용할 때 일반적으로 함수마다 별도의 스택이 있습니다. (기술적으로는 스택뿐만 아니라 전체 실행 컨텍스트는 함수별로 다릅니다. 가장 중요한 것은 CPU 레지스터입니다.) 모든 스레드에는 동시에 실행되는 함수만큼 많은 스택이 있으며 스레드는 각 함수 실행간에 전환됩니다. 프로그램의 논리에 따라. 함수가 끝까지 실행되면 스택이 파괴됩니다. 따라서 스택의 수와 수명은 동적이며 OS 수준 스레드의 수에 의해 결정되지 않습니다!
"보통 함수마다 별도의 스택이 있습니다"라고 언급했습니다. 쿠 루틴의 스택 및 스택리스 구현이 모두 있습니다. 가장 주목할만한 스택 C ++ 구현은 Boost.Coroutine 및 Microsoft PPL의 async / await입니다. (그러나 C ++ 17에 제안 된 C ++의 재개 가능한 함수 (일명 "async and await")는 스택리스 코 루틴을 사용할 가능성이 높습니다.)
C ++ 표준 라이브러리에 대한 Fibers 제안이 곧 나옵니다. 또한 일부 타사 라이브러리가 있습니다. 녹색 스레드는 Python 및 Ruby와 같은 언어에서 매우 인기가 있습니다.
|
주요 요점은 이미 다루었지만 공유 할 것이 있습니다.
스택
매우 빠른 액세스.
RAM에 저장됩니다.
함수 호출은 전달 된 지역 변수 및 함수 매개 변수와 함께 여기에로드됩니다.
프로그램이 범위를 벗어나면 공간이 자동으로 해제됩니다.
순차 메모리에 저장됩니다.
더미
Stack에 비교적 느리게 액세스합니다.
RAM에 저장됩니다.
동적으로 생성 된 변수는 여기에 저장되며 나중에 사용 후 할당 된 메모리를 해제해야합니다.
메모리 할당이 수행 될 때마다 저장되며 항상 포인터에 의해 액세스됩니다.
흥미로운 메모 :
함수 호출이 힙에 저장 되었다면 2 개의 지저분한 점이 발생했을 것입니다.
스택의 순차적 저장으로 인해 실행이 더 빠릅니다. 힙에 저장하면 시간이 많이 소모되어 전체 프로그램 실행 속도가 느려집니다.
함수가 힙 (포인터가 가리키는 지저분한 저장소)에 저장 되었다면 호출자 주소로 다시 돌아갈 수있는 방법이 없었을 것입니다 (메모리의 순차적 저장으로 인해 스택이 제공됨).
|
와! 답변이 너무 많고 그중 하나가 옳지 않은 것 같습니다 ...
1) 그것들은 어디에 있고 무엇입니까 (물리적으로 실제 컴퓨터의 메모리에 있음)?
스택은 프로그램 이미지에 할당 된 가장 높은 메모리 주소로 시작하여 값이 감소하는 메모리입니다. 호출 된 함수 매개 변수 및 함수에 사용되는 모든 임시 변수 용으로 예약되어 있습니다.
두 가지 힙 (공용 및 개인용)이 있습니다.
개인용 힙은 프로그램의 마지막 코드 바이트 이후 16 바이트 경계 (64 비트 프로그램의 경우) 또는 8 바이트 경계 (32 비트 프로그램의 경우)에서 시작하여 증가합니다.거기에서 가치. 기본 힙이라고도합니다.
프라이빗 힙이 너무 커지면 스택 영역과 겹치며 너무 커지면 스택이 힙과 겹칩니다. 스택은 더 높은 주소에서 시작하여 더 낮은 주소로 내려 가기 때문에 적절한 해킹을 통해 스택을 너무 크게 만들어 개인 힙 영역을 오버런하고 코드 영역을 겹칠 수 있습니다. 트릭은 코드에 연결할 수있는 코드 영역을 충분히 겹치는 것입니다. 수행하기가 약간 까다 롭고 프로그램 충돌의 위험이 있지만 쉽고 매우 효과적입니다.
공용 힙은 프로그램 이미지 공간 외부의 자체 메모리 공간에 있습니다. 메모리 리소스가 부족할 경우 하드 디스크로 빨아들이는 것은 바로이 메모리입니다.
2) OS 또는 언어 런타임에 의해 어느 정도까지 제어됩니까?
스택은 프로그래머가 제어하고, 프라이빗 힙은 OS에서 관리하며, 퍼블릭 힙은 OS 서비스이기 때문에 누구도 제어하지 않습니다. 요청을하면 승인되거나 거부됩니다.
2b) 그들의 범위는 무엇입니까?
그들은 모두 프로그램에 전역 적이지만 그 내용은 비공개, 공개 또는 전역일 수 있습니다.
2c) 각각의 크기를 결정하는 것은 무엇입니까?
스택 및 개인 힙의 크기는 컴파일러 런타임 옵션에 의해 결정됩니다. 공용 힙은 크기 매개 변수를 사용하여 런타임에 초기화됩니다.
2d) 무엇이 더 빨라 집니까?
빠르도록 설계된 것이 아니라 유용하도록 설계되었습니다. 프로그래머가 그것들을 활용하는 방법은 그들이 "빠른지" "느린 지"결정합니다.
참조 :
https://norasandler.com/2019/02/18/Write-a-Compiler-10.html
https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-getprocessheap
https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapcreate
|
많은 답변이 개념으로 정확하지만, 서브 루틴 호출 (어셈블리 언어로 CALL ..)을 허용하려면 하드웨어 (예 : 마이크로 프로세서)에 스택이 필요하다는 점에 유의해야합니다. (OOP 사람들은 그것을 메소드라고 부를 것입니다)
스택에서 반환 주소를 저장하고 호출 → push / ret → pop은 하드웨어에서 직접 관리됩니다.
스택을 사용하여 매개 변수를 전달할 수 있습니다. 레지스터를 사용하는 것보다 느리더라도 (마이크로 프로세서 전문가가 말하거나 1980 년대의 좋은 BIOS 북 ...)
스택이 없으면 마이크로 프로세서가 작동하지 않습니다. (어셈블리 언어로도 서브 루틴 / 함수없이 프로그램을 상상할 수 없습니다)
힙 없이는 할 수 있습니다. (어셈블리 언어 프로그램은 힙이 OS 개념이므로 malloc, 즉 OS / Lib 호출이 아니어도 작동 할 수 있습니다.
스택 사용량은 다음과 같이 더 빠릅니다.
하드웨어이며 푸시 / 팝도 매우 효율적입니다.
malloc은 커널 모드로 들어가고, 일부 코드를 실행하는 잠금 / 세마포어 (또는 다른 동기화 기본 요소)를 사용하고 할당을 추적하는 데 필요한 일부 구조를 관리해야합니다.
|
힙은 운영 체제 또는 메모리 관리자 라이브러리에서 자동으로 관리하는 동적 할당 메모리 영역입니다. 언제든지 블록을 할당하고 해제 할 수 있습니다. 힙 할당에는 할당 된 메모리와 할당되지 않은 메모리에 대한 전체 기록을 유지해야하며 조각화를 줄이고 요청 된 크기에 맞을만큼 큰 연속 메모리 세그먼트를 찾는 등의 오버 헤드 유지 관리가 필요합니다. 여유 공간을 남겨두고 언제든지 메모리를 할당 해제 할 수 있습니다. 힙이 커짐에 따라 새 블록은 종종 낮은 주소에서 높은 주소로 할당됩니다. 따라서 힙은 메모리가 할당됨에 따라 크기가 증가하는 메모리 블록의 힙으로 생각할 수 있습니다. 힙이 할당하기에 너무 작 으면 기본 운영 체제에서 더 많은 메모리를 확보하여 크기를 늘릴 수 있습니다. 힙에서 할당 된 메모리는 다음 중 하나가 발생할 때까지 할당 된 상태로 유지됩니다.
메모리가 해제됩니다.
프로그램이 종료됩니다.
스택:
힙처럼 컴퓨터 RAM에 저장됩니다.
스택에 생성 된 변수는 범위를 벗어나 자동으로 할당 해제됩니다.
힙의 변수에 비해 할당하는 것이 훨씬 빠릅니다.
매개 변수 전달에 사용되는 로컬 데이터, 리턴 주소를 저장합니다.
스택을 너무 많이 사용하면 스택 오버플로가 발생할 수 있습니다 (대부분
무한하거나 너무 깊은 재귀, 매우 큰 할당에서).
필요한 데이터의 양을 정확히 알고 있다면 스택을 사용합니다.
컴파일 시간 전에 할당하면 너무 크지 않습니다.
일반적으로 프로그램에서 이미 결정된 최대 크기가 있습니다.
시작합니다.
더미:
스택처럼 컴퓨터 RAM에 저장됩니다.
C ++에서 힙의 변수는 수동으로 삭제해야하며 절대로
범위를 벗어납니다.
데이터는 delete, delete [] 또는 free로 해제됩니다.
스택의 변수에 비해 할당 속도가 느립니다.
프로그램에서 사용할 데이터 블록을 할당하기 위해 요청시 사용됩니다.
많은 할당이있을 때 조각화 될 수 있고
할당 해제.
C ++ 또는 C에서 힙에 생성 된 데이터는 포인터로 가리 킵니다.
new 또는 malloc으로 각각 할당됩니다.
너무 큰 버퍼가 요청되면 할당 실패가 발생할 수 있습니다.
할당됩니다.
당신데이터의 양을 정확히 모르는 경우 힙을 사용합니다.
런타임시 또는 많은 데이터를 할당해야하는 경우 필요합니다.
메모리 누수를 담당합니다.
|
스택은 기본적으로 단순히 항목을 관리하는 액세스하기 쉬운 메모리입니다.
-잘-스택. 크기를 미리 알고있는 항목 만 스택에 올 수 있습니다. 이것은 숫자, 문자열, 부울의 경우입니다.
힙은 미리 결정할 수없는 항목에 대한 메모리입니다.
정확한 크기와 구조. 객체와 배열은 변경 될 수 있고
런타임에 변경되면 힙으로 이동해야합니다.
출처 : Academind
|
CPU 스택 및 힙은 CPU 및 레지스터가 메모리와 함께 작동하는 방식, 기계 어셈블리 언어가 작동하는 방식과 물리적으로 관련이 있습니다. 고급 언어 자체가 아닌 경우에도 이러한 언어가 작은 것을 결정할 수 있습니다.
모든 최신 CPU는 "동일한"마이크로 프로세서 이론에 따라 작동합니다. 이들은 모두 "레지스터"라고 불리는 것을 기반으로하고 일부는 성능을 얻기 위해 "스택"을위한 것입니다. 모든 CPU에는 처음부터 스택 레지스터가 있으며 내가 아는 것처럼 항상 여기에있었습니다. OO 가상 머신 어셈블리 언어를 갖도록 패러다임을 변경 한 Microsoft 및 IL (Intermediate Language)까지의 변형에도 불구하고 어셈블리 언어는 처음부터 동일합니다. 따라서 앞으로 일부 CLI / CIL CPU를 사용할 수 있습니다 (MS의 한 프로젝트).
CPU에는 메모리 액세스 속도를 높이기 위해 스택 레지스터가 있지만 프로세스에 사용 가능한 모든 메모리에 대한 전체 액세스를 얻기 위해 다른 레지스터를 사용하는 것에 비해 제한됩니다. 이것이 우리가 스택과 힙 할당에 대해 이야기 한 이유입니다.
요약하면, 일반적으로 힙은 느리고 느리며 스택이 작고 빠르며 "로컬"변수 및 참조 (관리하는 것을 잊는 숨겨진 포인터)를위한 "글로벌"인스턴스 및 객체 콘텐츠를위한 것입니다.
따라서 메서드에서 new 키워드를 사용할 때 참조 (int)가 스택에 생성되지만 객체와 모든 콘텐츠 (값 유형 및 객체)가 기억한다면 힙에 생성됩니다. 그러나 로컬 기본 값 유형 및 배열이 스택에 생성됩니다.
메모리 액세스의 차이는 셀 참조 수준에 있습니다. 프로세스의 전체 메모리 인 힙 주소 지정은 CPU 스택 때문에 주소 지정 측면에서 로컬로 "더 많은"스택보다 CPU 레지스터 처리 측면에서 더 많은 복잡성이 필요합니다. 내가 기억한다면 레지스터는 기본 주소로 사용됩니다.
이것이 우리가 매우 길거나 무한한 재귀 호출 또는 루프를 가질 때 현대 컴퓨터에서 시스템을 동결하지 않고 빠르게 스택 오버플로를 발생시키는 이유입니다.
.NET에서 C # 힙 (ing) 대 스택 (ing)
스택 대 힙 : 차이점 파악
저장되는 정적 클래스 메모리 할당 C #
스택과 힙은 무엇이며 어디에 있습니까?
https://en.wikipedia.org/wiki/Memory_management
https://en.wikipedia.org/wiki/Stack_register
어셈블리 언어 리소스 :
어셈블리 프로그래밍 튜토리얼
인텔 ® 64 및 IA-32 아키텍처 소프트웨어 개발자 설명서
|
정말 좋은 토론에 감사하지만 진짜 멍청한 사람으로서 지침이 어디에 보관되어 있는지 궁금합니다. 초기에 과학자들은 두 아키텍처 (모든 것이 데이터로 간주되는 von NEUMANN와 명령어를 위해 메모리 영역이 예약되고 데이터를 위해 다른 영역이 예약 된 HARVARD) 사이를 결정했습니다. 궁극적으로 우리는 von Neumann 디자인을 사용했고 이제 모든 것이 '동일한'것으로 간주됩니다. 이것은 내가 어셈블리를 배우는 동안 나를 힘들게 만들었다.
https://www.cs.virginia.edu/~evans/cs216/guides/x86.html
레지스터와 스택 포인터에 대해 이야기하기 때문입니다.
위의 모든 것은 DATA에 대해 이야기합니다. 내 생각에 명령어는 특정 메모리 풋 프린트를 가진 정의 된 것이므로 스택으로 이동하므로 어셈블리에서 논의 된 모든 '이러한'레지스터가 스택에 있습니다. 물론 명령과 데이터가 동적 인 구조로 들어온 객체 지향 프로그래밍이 나왔으므로 이제 명령도 힙에 유지 될까요?
|
매우 적극적인 질문입니다. 이 질문에 답하기 위해 평판 10을 획득하십시오. 평판 요구 사항은 스팸 및 비 응답 활동으로부터이 질문을 보호하는 데 도움이됩니다.
찾고있는 답변이 아닙니까? 메모리 관리 스택 언어에 구애받지 않는 힙 동적 메모리 할당 태그가 지정된 다른 질문을 찾아 보거나 직접 질문하십시오.